package org.dtc.geo.picture;

import static org.dtc.geo.picture.Constant.FIELD_MAPS;

import com.csvreader.CsvReader;
import com.google.common.collect.Lists;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.lang.reflect.Field;
import java.nio.charset.Charset;
import java.nio.file.Paths;
import java.util.List;
import java.util.Objects;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.tuple.Pair;
import org.openqa.selenium.By;
import org.openqa.selenium.OutputType;
import org.openqa.selenium.WebElement;

/**
 * @author zhouyu
 * @create 2024-01-08 10:12
 */
public class EnglishCollect {

    public static final String photoUrlPrefix = "https://webapps.bgs.ac.uk/data/MapsPortal/map.html?id=";

    public static final String resourcePath = "C:\\Users\\zhouyu\\IdeaProjects\\geo-picture\\src\\main\\resources\\english_collect";

    public static final String ID = "id";
    private Browser browser;

    public EnglishCollect() {
        this.browser = new Browser();
    }

    public PhotoMetaData getPhotoPreviewUrl(String photoUrl, List<String> existIds)
            throws NoSuchFieldException, IllegalAccessException, InterruptedException, IOException {
        browser.getUrl(photoUrl);
        sleep(15);
        WebElement tableElement = browser.findElement(By.xpath("//table"));
        WebElement tbodyElement = tableElement.findElement(By.tagName("tbody"));
        List<WebElement> trElements = tbodyElement.findElements(By.tagName("tr"));
        PhotoMetaData photoMetaData = new PhotoMetaData();
        for (WebElement trElement : trElements) {
            if (!FIELD_MAPS.containsKey(trElement.getAttribute(ID))) {
                continue;
            }
            String value = trElement.findElement(By.tagName("td")).getText();
            if (StringUtils.isBlank(value)) {
                continue;
            }
            String fieldName = FIELD_MAPS.get(trElement.getAttribute(ID));
            Field field = PhotoMetaData.class.getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(photoMetaData, value);
        }

        WebElement tdaElement = tbodyElement.findElement(By.xpath("//td/a[@class='image-link']"));
        String preViewUrl = tdaElement.getAttribute("href");
        photoMetaData.setUrl(photoUrl);
        photoMetaData.setPreviewUrl(preViewUrl);
        photoMetaData.setId(parsePreviewId(preViewUrl));

        //已经下载过的不需要下载
        if(existIds.contains(photoMetaData.getId())) {
            return null;
        }

        browser.getUrl(preViewUrl);
        sleep(45);
        WebElement canvasElement = browser.findElement(By.xpath("//div[@class='canvas']"));
        File imageFile = canvasElement.getScreenshotAs(OutputType.FILE);
        FileUtils.copyFile(imageFile,
                new File(Paths.get(resourcePath, "photo_data", String.format("%s.png", photoMetaData.getId())).toString()));
        return photoMetaData;
    }

    public void getPhotoPreviewUrls() throws IOException{
        String readPath = Paths.get(resourcePath, "photo_urls.txt").toString();
        BufferedReader reader = new BufferedReader(new FileReader(readPath));
        List<String> urlList = Lists.newArrayList();
        String line = null;
        while ((line = reader.readLine()) != null) {
            urlList.add(line);
        }

        String metaReadPath = Paths.get(resourcePath, "photo_data", "meta.csv").toString();
        List<String> existIds = getExistIds(metaReadPath);
        boolean metaFileEmpty = false;

        File metaReadFile = new File(metaReadPath);
        if(metaReadFile.length() == 0) {
            metaFileEmpty = true;
        }

        String errorWritePath = Paths.get(resourcePath, "error.txt").toString();
        BufferedWriter errorWriter = new BufferedWriter(new FileWriter(errorWritePath, false));

        BufferedWriter metaWriter = new BufferedWriter(new FileWriter(metaReadPath, true));

        if(metaFileEmpty) {
            metaWriter.write(PhotoMetaData.getHeaderInfo() + "\n");
        }

        for(String url : urlList) {
            try {
                PhotoMetaData photoMetaData = getPhotoPreviewUrl(url, existIds);
                if(Objects.isNull(photoMetaData)) {
                    continue;
                }
                metaWriter.write(photoMetaData + "\n");
                metaWriter.flush();
            } catch (Exception e) {
                errorWriter.write(url + "\n");
                errorWriter.write(e.getMessage() + "\n");
                errorWriter.flush();
            }
        }

        errorWriter.close();
        metaWriter.close();
        reader.close();
    }

    public List<String> getPhotoUrl(String url) {
        try {
            List<String> result = Lists.newArrayList();
            browser.getUrl(url);
//            sleep(30);
            while (true) {
                WebElement tableElement = browser.findElement(By.xpath("//table"));
                WebElement tbodyElement = tableElement.findElement(By.tagName("tbody"));
                List<WebElement> trElements = tbodyElement.findElements(By.tagName("tr"));
                for (WebElement trElement : trElements) {
                    String id = trElement.getAttribute("id");
                    result.add(photoUrlPrefix + id);
                }
                WebElement spanPagesElement = tableElement.findElement(By.xpath("//caption/div/span[@class='pages']"));
                String pageInfo = spanPagesElement.getText();
                Pair<Integer, Integer> pageInfoPair = parsePageInfo(pageInfo);

                WebElement spanPageNavElement = tableElement.findElement(By.xpath("//caption/div/span[@class='page-nav']"));

                List<WebElement> inputElements = spanPageNavElement.findElements(By.tagName("input"));
                WebElement nextInputElement = null;
                for (WebElement inputElement : inputElements) {
                    if (inputElement.getAttribute("value").equals(String.valueOf(pageInfoPair.getLeft() + 1))) {
                        nextInputElement = inputElement;
                        break;
                    }
                }
                if (Objects.isNull(nextInputElement)) {
                    break;
                }
                nextInputElement.click();
//                sleep(30);
            }
            return result;
        } catch (Exception e) {
            System.out.printf("getPhotoUrl error, url:%s\n", url);
            return Lists.newArrayList();
        }
    }

    public void generatePhotoUrls() {
        try {

            List<String> urlList = Lists.newArrayList();

            String writePath = Paths.get(resourcePath, "photo_urls.txt").toString();
            BufferedWriter writer = new BufferedWriter(new FileWriter(writePath, false));

            String readPath = Paths.get(resourcePath, "download_urls.txt").toString();
            BufferedReader reader = new BufferedReader(new FileReader(readPath));

            String line = null;
            while ((line = reader.readLine()) != null) {
                urlList.add(line.split(";")[1]);
            }
            for (String url : urlList) {
                List<String> photoUrls = getPhotoUrl(url);
                for (String photoUrl : photoUrls) {
                    writer.write(photoUrl + "\n");
                }
            }
            writer.close();
            reader.close();
        } catch (Exception e) {
            System.out.printf("generatePhotoUrls error: message:%s\n", e.getMessage());
        }
    }

    private void sleep(int time) throws InterruptedException {
        Thread.sleep(time * 1000L);
    }

    private Pair<Integer, Integer> parsePageInfo(String pageInfo) {
        String patternStr = "Page (\\d+) of (\\d+)";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher m = pattern.matcher(pageInfo);
        if (m.find()) {
            return Pair.of(Integer.parseInt(m.group(1)), Integer.parseInt(m.group(2)));
        } else {
            throw new RuntimeException("parsePageInfo error");
        }
    }

    private String parsePreviewId(String url) {
        String patternStr = ".*?id=(\\d+)";
        Pattern pattern = Pattern.compile(patternStr);
        Matcher m = pattern.matcher(url);
        if (m.find()) {
            return m.group(1);
        } else {
            throw new RuntimeException("parsePreviewId error");
        }
    }

    private List<String> getExistIds(String csvPath) {
        try {
            CsvReader csvReader = new CsvReader(csvPath, '\t', Charset.defaultCharset());
            csvReader.readHeaders();
            List<String> existIds = Lists.newArrayList();
            while (csvReader.readRecord()) {
                existIds.add(csvReader.getValues()[0]);
            }
            return existIds;
        } catch (Exception e) {
            System.out.printf("getExistIds error, message: %s", e.getMessage());
            return Lists.newArrayList();
        }
    }
}

